package com.example.java.base.concurrency.flowcontrol.countdownlatch;

import com.example.java.base.concurrency.flowcontrol.MyThreadFactory;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * CountDownLatchDemo:并发流程控制之-递减计数器(锁存器),用法1 :一等多类型(多个是并行)
 * 使用场景一件商品等待多名客户拼团
 * 一局游戏等待多名玩家同时在线 一场新人培训等待多名入职者同时入职公司 一次体检等待多个项目完成
 *
 * @author zhangxiaoxiang
 * @date 2021/04/20
 */
@Slf4j
public class CountDownLatchDemo {
    @SneakyThrows
    public static void main(String[] args) {
        //等几次
        CountDownLatch latch = new CountDownLatch(5);
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                5,
                5,
                //60秒不使用就回收空闲线程
                60L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(5),
                new MyThreadFactory());
        for (int i = 1; i <= 5; i++) {
            final int no = i;
            Runnable runnable = () -> {
                try {
                    //模拟业务耗时
                    Thread.sleep((long) (Math.random() * 10000));
                    log.info("{}号拼夕夕用户加入了拼购活动", no);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //减少锁存器的计数，如果计数达到零，释放所有等待的线程。 如果当前计数大于零，那么它将递减。
                    latch.countDown();
                    log.info("===>商家提示:还差{}名用户,即可发货", latch.getCount());
                }
            };
            executor.submit(runnable);
        }
        log.info("--------发起5人拼购,拼团中(即等待线程处理处理)--------");
        //导致当前线程等待，直到锁存器递减计数到零为止，除非该线程被中断
        latch.await();
        log.info("所有的用户都加入拼团,满足条件,商家开始发货");
        //关闭线程池(根据实际来)
        // executor.shutdown();
        // log.info("关闭线程池");
        if (!executor.isShutdown()) {
            executor.submit(() -> log.info("线程池没有显式关闭,可以再次提交任务进去"));
        } else {
            log.info("线程池已经关闭!");
        }
    }
}
